#include "grid.h"


namespace vkrender
{
    std::vector<VkVertexInputBindingDescription> Grid::Vertex::getBindingDescriptions()
    {
        std::vector<VkVertexInputBindingDescription> bindingDescriptions(1);
        bindingDescriptions[0].binding = 0;
        bindingDescriptions[0].inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
        bindingDescriptions[0].stride = sizeof(Vertex);
        return bindingDescriptions;
    }

    std::vector<VkVertexInputAttributeDescription> Grid::Vertex::getAttributeDescriptions()
    {
        std::vector<VkVertexInputAttributeDescription> attributeDescriptions(2);
        attributeDescriptions[0].binding = 0;
        attributeDescriptions[0].location = 0;
        attributeDescriptions[0].format = VK_FORMAT_R32G32B32_SFLOAT;
        attributeDescriptions[0].offset = offsetof(Vertex, pos);

        attributeDescriptions[1].binding = 0;
        attributeDescriptions[1].location = 1;
        attributeDescriptions[1].format = VK_FORMAT_R32G32B32_SFLOAT;
        attributeDescriptions[1].offset = offsetof(Vertex, color);
        return attributeDescriptions;
    }

    Grid::Grid(const std::shared_ptr<VulkanDeviceContext>& context)
    {
        this->context = context;
        stride = 0.5f;
        bounds.x = -10;
        bounds.y = 10;
        bounds.z = -10;
        bounds.w = 10;
        color = glm::vec3(1.0f, 1.0f, 1.0f);
        createBuffers();
    }

    void Grid::createBuffers()
    {
        std::vector<Vertex> vertices;
        std::vector<uint32_t> indices;
        int xmin = bounds.x;
        int xmax = bounds.y;
        int ymin = bounds.z;
        int ymax = bounds.w;
        uint32_t index = 0;
        uint32_t width = (xmax - xmin + 1);
        for (int y = ymin; y <= ymax; ++y)
        {
            for (int x = xmin; x <= xmax; ++x)
            {
                // add vertex
                glm::vec3 pos = { x * stride, 0.5, y * stride};
                vertices.push_back({ pos, color });
                // add horizontal edge
                if (x < xmax)
                {
                    indices.push_back(index);
                    indices.push_back(index + 1);
                }
                if (y < ymax)
                {
                    indices.push_back(index);
                    indices.push_back(index + width);
                }
                index += 1;
            }
        }

        // do not using staging buffer.
        vertexCount = static_cast<uint32_t>(vertices.size());
        VkDeviceSize bufferSize = sizeof(vertices[0]) * vertexCount;

        this->context->createBuffer(
            bufferSize,
            VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
            VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
            vertexBuffer, vertexBufferMemory);

        void* data;
        vkMapMemory(context->device, vertexBufferMemory, 0, bufferSize, 0, &data);
        memcpy(data, vertices.data(), static_cast<size_t>(bufferSize));
        vkUnmapMemory(context->device, vertexBufferMemory);

        indexCount = static_cast<uint32_t>(indices.size());
        VkDeviceSize indexbuffersize = sizeof(uint32_t) * indexCount;

        this->context->createBuffer(
            indexbuffersize, VK_BUFFER_USAGE_INDEX_BUFFER_BIT,
            VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
            indexBuffer, indexBufferMemory);

        vkMapMemory(context->device, indexBufferMemory, 0, indexbuffersize, 0, &data);
        memcpy(data, indices.data(), static_cast<size_t>(indexbuffersize));
        vkUnmapMemory(context->device, indexBufferMemory);
    }

    void Grid::bind(VkCommandBuffer commandBuffer) const
    {
        VkBuffer buffers[] = { vertexBuffer };
        VkDeviceSize offsets[] = { 0 };
        vkCmdBindVertexBuffers(commandBuffer, 0, 1, buffers, offsets);
        vkCmdBindIndexBuffer(commandBuffer, indexBuffer, 0, VkIndexType::VK_INDEX_TYPE_UINT32);
    }
    
    void Grid::draw(VkCommandBuffer commandBuffer) const
    {
        vkCmdDrawIndexed(commandBuffer, indexCount, 1, 0, 0, 0);
    }
}
